# -*-perl-*-
# $Id$
# Copyright (C) 2002-2005 Freescale Semiconductor, Inc.
# Distributed under terms of the Perl license, which is the disjunction of
# the GNU General Public License (GPL) and the Artistic License.

# Writer for xref files

=pod
=begin reST
=begin Description
This writer exports cross reference targets defined in the source
reStructuredText (reST) file. The output is in reST format and
includes:

1. All non-anonymous internal targets, exported as ::

     .. _<targetName>: <base-file><ext>.<suffix>#<targetID>

   Here <base-file> is the base file name (file name without extension).

2. All citations, exported as::

     .. _<citationName>: <base-file><ext>.<suffix>#<citationID>

3. Substitution definitions that have the same names as internal
   targets but do not, directly or indirectly through substitution
   references, define internal target or refer to external
   references or footnotes.

4. Definition of substitutions referred to by the substitutions
   exported according to 3. Substitution definitions are exported
   as defined in the source file.

5. If -W xref-sections is specified,

   a. Targets for all section titles, exported as ::

        .. _<base-file>.<base-title>: <base-file><ext>.<suffix>#<titleID>

      Here <base-title> is the title name prior to any autonumbering.

   b. Substitution definitions for all section titles, exported as ::

        .. |<base-file>.<base-title>| replace:: <section-title>

   c. Target for the file itself, exported as ::

        .. _<base-file>.: <sourceFileName><ext>.<suffix>

   d. Substitution definition for the file itself, exported as ::

       .. |<base-file>.| replace:: <document-title>

6. If -W xref-role-targets is specified, then inline targets that are
   defined through an interpreted text target role are included in the
   index.

This writer uses the following output definitions:

-W appendix=<string>     String to use for an appendix reference when
                         xref-sections=1 (default "Appendix").  An
                         appendix is a document with a
                         "number-prefixed" title that is not a number.
-W chapter=<string>      String to use for a chapter reference when
                         xref-sections=1 (default "Chapter").  A
                         chapter is a document with a
                         "number-prefixed" title that is a number.
-W file-suffix=<suffix>  Specify a file suffix to be used for
                         exporting the cross reference targets
                         (default is html).
-W filename-ext=<ext>    Specify an extension to the filename,
                         (e.g. "_main") so the file location of
                         targets becomes <file><ext>.<suffix>
                         (default is "").
-W file-path=<dir>       Specify additional path information for
                         the target file (default is "")
-W section=<string>      String to use for a section reference when
                         xref-sections=1 (default "Section").
-W sprintf=<string>      Specify an sprintf string for formatting the
                         output definitions (default is "%s").
-W xref-role-target[=<name>]
                         Output cross-references for non-empty
                         internal targets created from interpreted
                         text roles (default is 0, or if specified
                         with no name, 'target').
-W xref-sections=<0|1>   Output cross-reference for section titles
                         (default is 0).
-W xref-targets=<0|1>    Output cross-reference for internal targets
                         (default is 1)
=end Description
=end reST
=cut

sub BEGIN = {
    # My -W flags
    use vars qw($appendix $chapter $file_suffix $filename_ext $file_path
		$section $sprintf $xref_role_target
                $xref_sections $xref_targets);

    # Run-time globals
    use vars qw($BASE_FILE $OUT_FILE %TARGET_NAMES @TARGETS $XREF_STRS);

    # Defaults for -W flags
    $appendix          = 'Appendix' unless defined $appendix;
    $chapter           = 'Chapter'  unless defined $chapter;
    $file_suffix       = 'html'     unless defined $file_suffix;
    $filename_ext      = ''         unless defined $filename_ext;
    $file_path         = ''         unless defined $file_path;
    $section           = 'Section'  unless defined $section;
    $sprintf           = "%s"       unless defined $sprintf;
    $xref_role_target  = 0          unless defined $xref_role_target;
    $xref_role_target  = 'target'   if ref $xref_role_target eq 'SCALAR';
    $xref_sections     = 0          unless defined $xref_sections;
    $xref_targets      = 1          unless defined $xref_targets;

    $file_path .= "/" if ($file_path && $file_path !~ /\/$/);
}

sub exportableDef {
    my ($dom) = @_;
    # uncoverable branch true note:It will probably never be defined
    return $dom->{_xref}{exportable}
    if defined $dom->{_xref}{exportable};
    my @contents = $dom->contents();
    $dom->{_xref}{exportable} = "true";
    foreach my $child (@contents) {
	my $ctag = $child->tag;
	if ($ctag eq 'target' ||
	    $ctag eq 'footnote_reference' ||
	    $ctag eq 'problematic' ||
	    ($ctag eq 'reference' && $child->{attr}{refuri})) {
	    $dom->{_xref}{exportable} = "false";
	}
    }
    return $dom->{_xref}{exportable};
}

# During the first pass, write all non-anonymous internal targets and
# citations out.
phase PREPROCESS {
    sub document = {
	my ($dom, $str) = @_;
	$OUT_FILE = $BASE_FILE = $dom->{attr}{source};
	$BASE_FILE =~ s/\..*?$//;
	$OUT_FILE .= "$filename_ext.$file_suffix"
	    if (!($OUT_FILE =~ s/\..*?$/$filename_ext\.$file_suffix/o));
	return unless $xref_targets;
	my $str2;
	my ($key, $value);
	foreach (@TARGETS) {
	    my ($name, $id) = @$_;
	    $str2 .= sprintf "$sprintf\n", ".. _`$name`: $file_path$OUT_FILE#$id";
	}
	return $str2;
    }

    # Save all citations in @TARGETS array. The elements of the array
    # are references to two-element (name, refid) pairs.
    sub citation|paragraph = {
	my ($dom, $str) = @_;
	my @names = grep(!$TARGET_NAMES{$_},
			 @{$dom->{attr}{names}}) if $dom->{attr}{names};
	# Note: the following is faking a method call with a null obj.
	# It's safe because this method should not require its object
	# in this context.
	push @TARGETS, map
	    [$_, Text::Restructured::NormalizeId(0, $_)], @names;
	@TARGET_NAMES{@names} = (1) x @names;
	return;
    }

    sub section = {
	my ($dom, $str) = @_;
	# Note; the first name in {attr}{names} is the section name itself;
	# any succeeding elements were attached by targets directed to the
	# section.
	# uncoverable branch false note:Expect always to have names
	my @names = @{$dom->{attr}{names}} if $dom->{attr}{names};
	shift @names;
	@names = grep(!$TARGET_NAMES{$_}, @names);
	push @TARGETS, map
	    [$_, Text::Restructured::NormalizeId(0, $_)], @names;
	@TARGET_NAMES{@names} = (1) x @names;
	return;
    }

    # Save non-anonymous internal targets into @TARGETS array. The
    # elements of the array are references to two-element (name, id)
    # pairs (for direct targets) or (name, refid) pairs (for indirect
    # targets).
    sub target = {
	my ($dom, $str) = @_;
	my $attr = $dom->{attr};
	if (!$attr->{refuri} && !$attr->{anonymous} &&
	    (!$attr->{role} || $attr->{role} eq $xref_role_target)) {
	    $attr->{names} = $attr->{dupnames}
	        if defined $attr->{dupnames} && !defined $attr->{names};
	    my @names = grep(!$TARGET_NAMES{$_},
			     @{$dom->{attr}{names}}) if $dom->{attr}{names};
	    my $refid = $attr->{refid};
	    # Devel::Cover condition 0 2 NormalizeId is always true
	    # uncoverable condition false note:NormalizeId is always true
	    push @TARGETS, map
		[$_, $refid || Text::Restructured::NormalizeId(0, $_)], @names;
	    @TARGET_NAMES{@names} = (1) x @names;
	}
 	return;
    }
}

# During the second pass, handle substitution definitions. Export a
# definition if both of these conditions are true.
# o. It has the same name as an internal target or citation, or is
#    referred to (directly or indirectly) by another definition that 
#    has the same name as an internal target.
# o. It does not contain (directly or indirectly) footnote and external 
#    references, internal target, or problematic elements.
# Also, handle xref-sections=1
phase PROCESS {
    sub document = {
	my ($dom, $str) = @_;
	if ($xref_sections) {
	    $XREF_STRS .= ".. _$BASE_FILE.: $file_path$OUT_FILE\n";
	    my $title = '';
	    $dom->first->Recurse(sub {
		my ($dom) = @_;
		$title .= $dom->{text} if $dom->tag eq '#PCDATA';
	    }) if $dom->first->tag eq 'title';
            $title = $dom->{attr}{title} || '' unless $title;
	    $title =~ s/^(.*?)\.?[\xa0\xc2]+/ /;
	    my ($number) = ($1);
	    my $ref = ! defined $number ? $title :
		$number =~ /\d$/ ? "$chapter $number" : "$appendix $number";
	    $XREF_STRS .= ".. |$BASE_FILE.| replace:: $ref\n";
	}
	return $XREF_STRS;
    }

    sub section = {
	my ($dom, $str) = @_;
	if ($xref_sections) {
	    my $name = $dom->{attr}{names}[0];
	    return if ! defined $name;
	    $XREF_STRS .= (".. _$BASE_FILE.$name: $file_path$OUT_FILE#" .
			   Text::Restructured::NormalizeId(0, $name) . "\n");
	    my $title = '';
	    $dom->first->Recurse(sub {
		my ($dom) = @_;
		$title .= $dom->{text} if $dom->tag eq '#PCDATA';
	    });
	    $title =~ s/^((.*?)(\..*?)?)[\xa0\xc2]+/ /;
	    my ($number, $prefix, $suffix) = ($1, $2, $3);
	    my $ref = ! defined $number ? $title :
		defined $suffix ? "$section $number" :
		$prefix =~ /\d$/ ?"$chapter $number" : "$appendix $number";
	    $XREF_STRS .= ".. |$BASE_FILE.$name| replace:: $ref\n";
	}
    }

    sub substitution_definition = {
	my ($dom, $str) = @_;
 	# Do nothing if this definition has been exported, has been
 	# previously identified as not exportable, or does not have
 	# the same name as an internal target or citation.
	return unless $xref_targets;
	# Devel::Cover +2 condition 0 1 null string always false
	return 
	    if ($dom->{_xref}{exported} ||
		($dom->{_xref}{exportable} || '') eq "false" ||
		!defined $TARGET_NAMES{lc $dom->{attr}{names}[0]});

 	return if (exportableDef($dom) eq "false");
	$XREF_STRS .= $dom->{lit} . "\n";
	$dom->{_xref}{exported} = 1;
	return;
    }
}
